package com.jcp.baselibrary.okhttp3;

import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jcp.baselibrary.base.BaseApplication;
import com.jcp.baselibrary.okhttp3.GsonAdapter.DoubleDefaultAdapter;
import com.jcp.baselibrary.okhttp3.GsonAdapter.IntegerDefaultAdapter;
import com.jcp.baselibrary.okhttp3.GsonAdapter.LongDefaultAdapter;
import com.jcp.baselibrary.okhttp3.GsonAdapter.StringDefaultAdapter;
import com.jcp.baselibrary.okhttp3.cookie.PersistentCookieJar;
import com.jcp.baselibrary.okhttp3.https.HttpsUtils;
import com.jcp.baselibrary.okhttp3.log.LoggerInterceptor;
import com.jcp.baselibrary.okhttp3.progress.ProgressRequestBody;
import com.jcp.baselibrary.okhttp3.response.OkHttpCallback;
import com.jcp.baselibrary.utils.MyLogger;
import com.jcp.baselibrary.utils.UIUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;

/**
 * Created by ldy on 2015/12/29.
 */
public class OkHttpUtil {

    private static final String TAG = OkHttpUtil.class.getSimpleName();
    public static final int DEFAULT_CONNECT_TIMEOUT = 20 * 1000;
    public static final int DEFAULT_READ_TIMEOUT = 20 * 1000;
    public static final int DEFAULT_WRITE_TIMEOUT = 20 * 1000;

    private static final long cacheSize = 1024 * 1024 * 5;//缓存文件最大限制大小5M
    private static String cacheDirectory = BaseApplication.getAppCacheDir().getAbsolutePath();  //设置缓存文件路径
    private static Cache cache = new Cache(new File(cacheDirectory), cacheSize);  //

    private static OkHttpClient mOkHttpClient = null;
    private static Gson gson = null;
    private static Handler mOkHandler = null;

    public static Callback callback;
    private static Request request;

    public static void init(OkHttpClient.Builder builder) {
        if (builder == null) {
            builder = getBuilder(false);
        }
        if (mOkHttpClient == null) {
            mOkHttpClient = builder.build();
        }
    }

    public static OkHttpClient getOkHttpClient() {
        return mOkHttpClient;
    }

    public static OkHttpClient.Builder getBuilder(boolean isHttps) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS);
        builder.readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.MILLISECONDS);
        builder.writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.MILLISECONDS);
        // 错误重连
        builder.retryOnConnectionFailure(true);
        //设置缓存
        builder.cache(cache);
        //设置cookies
        builder.cookieJar(new PersistentCookieJar(UIUtils.getContext()));
        //设置log拦截器
        builder.addInterceptor(new LoggerInterceptor(true));
        if (isHttps) {
            HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
            builder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
        }
        return builder;
    }

    public static Gson buildGson() {
        if (gson == null) {
            gson = new GsonBuilder()
                    .registerTypeAdapter(Integer.class, new IntegerDefaultAdapter())
                    .registerTypeAdapter(int.class, new IntegerDefaultAdapter())
                    .registerTypeAdapter(Double.class, new DoubleDefaultAdapter())
                    .registerTypeAdapter(double.class, new DoubleDefaultAdapter())
                    .registerTypeAdapter(Long.class, new LongDefaultAdapter())
                    .registerTypeAdapter(long.class, new LongDefaultAdapter())
                    .serializeNulls()
                    .create();
        }
        return gson;
    }


    public static <T> void get(String url, OkHttpCallback<T> okHttpCallback) {
        get(url, null, okHttpCallback);
    }

    public static <T> void get(String url, OkRequestParams params, OkHttpCallback<T> okHttpCallback) {
        get(url, params, null, okHttpCallback);
    }

    public static <T> void get(String url, Object tag, OkHttpCallback<T> okHttpCallback) {
        get(url, null, tag, okHttpCallback);
    }

    public static <T> void get(String url, OkRequestParams params, Object tag, OkHttpCallback<T> okHttpCallback) {
        if (mOkHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        Call call = null;
        callback = getCallBack(okHttpCallback);
        try {
            Headers headers = getRequestHeaders(params);
            request = getRequest(getFinalUrl(url, params), null, headers, tag);

            call = mOkHttpClient.newCall(request);
            call.enqueue(callback);
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onFailure(call, new IOException("get", e));
        }
    }

    public static <T> void post(String url, OkHttpCallback<T> okHttpCallback) {
        post(url, null, okHttpCallback);
    }

    public static <T> void post(String url, OkRequestParams params, OkHttpCallback<T> okHttpCallback) {
        post(url, params, null, okHttpCallback);
    }

    public static <T> void post(String url, OkRequestParams params, Object tag, OkHttpCallback<T> okHttpCallback) {
        post(url, params, tag, false, okHttpCallback);
    }

    public static <T> void post(String url, OkRequestParams params, Object tag, boolean isProgress, OkHttpCallback<T> okHttpCallback) {
        if (mOkHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        Call call = null;
        callback = getCallBack(okHttpCallback);
        try {
            RequestBody requestBody = getRequestBody(params, isProgress, okHttpCallback);
            Headers headers = getRequestHeaders(params);
            request = getRequest(url, requestBody, headers, tag);

            call = mOkHttpClient.newCall(request);
            call.enqueue(callback);
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onFailure(call, new IOException("post", e));
        }
    }

    public static <T> void syncPost(String url, OkHttpCallback<T> okHttpCallback) {
        syncPost(url, null, okHttpCallback);
    }

    public static <T> void syncPost(String url, OkRequestParams params, OkHttpCallback<T> okHttpCallback) {
        syncPost(url, params, null, okHttpCallback);
    }

    public static <T> void syncPost(String url, OkRequestParams params, Object tag, OkHttpCallback<T> okHttpCallback) {
        syncPost(url, params, tag, false, okHttpCallback);
    }

    public static <T> void syncPost(String url, OkRequestParams params, Object tag, boolean isProgress, OkHttpCallback<T> okHttpCallback) {
        if (mOkHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        Call call = null;
        callback = getCallBack(okHttpCallback);
        try {
            RequestBody requestBody = getRequestBody(params, isProgress, okHttpCallback);
            Headers headers = getRequestHeaders(params);
            request = getRequest(url, requestBody, headers, tag);

            call = mOkHttpClient.newCall(request);
            Response response = call.execute();
            if (response != null) {
                callback.onResponse(call, response);
            } else {
                callback.onFailure(call, new IOException("syncPost: response == null"));
            }
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onFailure(call, new IOException("syncPost", e));
        }
    }

    public static void restartRequest() {
        MyLogger.e(TAG, "restartRequest");
        if (request == null && callback == null && mOkHttpClient == null) return;
        Call call = null;
        try {
            call = mOkHttpClient.newCall(request);
            call.enqueue(callback);
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onFailure(call, new IOException("restartRequest", e));
        }
    }


    public static void cancelTag(Object tag) {
        if (mOkHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        try {
            for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
                if (tag.equals(call.request().tag())) {
                    call.cancel();
                }
            }
            for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
                if (tag.equals(call.request().tag())) {
                    call.cancel();
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static <T> Callback getCallBack(OkHttpCallback<T> okHttpCallBack) {
        if (okHttpCallBack == null) {
            return OkHttpCallback.DEFAULT_CALLBACK;
        } else {
            okHttpCallBack.setHandler(getOkHandler());
            return okHttpCallBack;
        }
    }

    public static Request getRequest(String url, RequestBody requestBody, Headers headers, Object tag) {
        Request.Builder builder = new Request.Builder();
        if (requestBody != null) {
            builder.post(requestBody);
        }
        if (headers != null) {
            builder.headers(headers);
        }
        if (tag != null) {
            builder.tag(tag);
        }
        builder.url(url);
        return builder.build();
    }

    public static String getFinalUrl(String url, OkRequestParams params) {
        if (params != null) {
            String paramString = params.getParamString().trim();
            if (!paramString.equals("") && !paramString.equals("?")) {
                url += url.contains("?") ? "&" : "?";
                url += paramString;
            }
        }
        return url;
    }

    public static Headers getRequestHeaders(OkRequestParams params) {
        return params == null ? null : params.getRequestHeaders();
    }

    public static RequestBody getRequestBody(OkRequestParams params) {
        return params == null ? null : params.getRequestBody();
    }

    public static <T> RequestBody getRequestBody(OkRequestParams params, boolean isProgress, OkHttpCallback<T> okHttpCallback) {
        RequestBody requestBody = getRequestBody(params);
        if (requestBody != null && isProgress && okHttpCallback != null) {
            requestBody = new ProgressRequestBody(requestBody, okHttpCallback);
        }
        return requestBody;
    }

    public static Handler getOkHandler() {
        if (mOkHandler == null) {
            mOkHandler = new Handler(Looper.getMainLooper());
        }
        return mOkHandler;
    }
}
